package avicit.bdp.dds.server.worker.task.algorithm;

import avicit.bdp.common.utils.ConfigUtils;
import avicit.bdp.common.utils.SpringApplicationContext;
import avicit.bdp.common.utils.uploads.FileAdapterUtils;
import avicit.bdp.common.utils.uploads.IFileAdapter;
import avicit.bdp.dds.dispatch.task.AbstractParameters;
import avicit.bdp.dds.dispatch.task.algorithm.AlgorithmParameters;
import avicit.bdp.core.util.json.JSONUtils;
import avicit.bdp.dds.common.Constants;
import avicit.bdp.dds.common.utils.FileUtils;
import avicit.bdp.dds.common.utils.ParameterUtils;
import avicit.bdp.dds.dao.entity.AlgoNodes;
import avicit.bdp.dds.dao.entity.Resource;
import avicit.bdp.dds.dao.entity.TaskNodes;
import avicit.bdp.dds.server.entity.TaskExecutionContext;
import avicit.bdp.dds.server.worker.task.AbstractTask;
import avicit.bdp.dds.server.worker.task.CommandExecuteResult;
import avicit.bdp.dds.server.worker.task.ShellCommandExecutor;
import avicit.bdp.dds.service.process.ProcessService;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/** python task */
public class AlgorithmPythonTask extends AbstractTask {

  /** python parameters */
  private AlgorithmParameters algorithmParameters;

  /** task dir */
  private String taskDir;

  /** python command executor */
  private ShellCommandExecutor shellCommandExecutor;

  /** taskExecutionContext */
  private final TaskExecutionContext taskExecutionContext;

  private final ProcessService processService;

  private String pythonFileName;
  private String mainArgs;

  /** python */
  public static final String PYTHON = "python";

  /**
   * constructor
   *
   * @param taskExecutionContext taskExecutionContext
   * @param logger logger
   */
  public AlgorithmPythonTask(TaskExecutionContext taskExecutionContext, Logger logger) {
    super(taskExecutionContext, logger);
    this.taskExecutionContext = taskExecutionContext;

    this.processService = SpringApplicationContext.getBean(ProcessService.class);

    this.shellCommandExecutor =
        new ShellCommandExecutor(this::logHandle, taskExecutionContext, logger);
  }

  @Override
  public void init() throws IOException {
    logger.info("python task params {}", taskExecutionContext.getTaskParams());

    algorithmParameters =
        JSONUtils.parseObject(taskExecutionContext.getTaskParams(), AlgorithmParameters.class);

    if (algorithmParameters == null || !algorithmParameters.checkParameters()) {
      throw new RuntimeException("python task params is not valid");
    }
    Integer programType = null;
    Integer type = algorithmParameters.getType();
    String nodeId = algorithmParameters.getNodeId();
    String resourceName;
    String execClassName = "";
    // 算法库算法.
    if (AlgorithmParameters.CUSTOM_ALGORITHM_LIBRARY.equals(type)) {
      AlgoNodes algoNodes = processService.getTaskAlgoNodesById(nodeId);
      Resource resource = processService.getResourceById(algoNodes.getResourceId());
      if (resource == null) {
        logger.error("resource id: {} not exist", algoNodes.getResourceId());
        throw new RuntimeException(
            String.format("resource id: %s not exist", algoNodes.getResourceId()));
      }
      // 下载jar包
      downloadResource(taskExecutionContext.getExecutePath(), resource.getFullName());

      resourceName = resource.getFullName().replaceFirst("/", "");
      programType = algoNodes.getProgramType();
      execClassName = algoNodes.getClassName();
    } else {
      TaskNodes taskNodes = processService.getTaskNodesById(nodeId);
      // 下载jar包
      // downloadResource(taskExecutionContext.getExecutePath(), taskNodes.getNodeFilePath());
      FileUtils.copyResourceFile(
          taskExecutionContext.getExecutePath(), "/libs/" + taskNodes.getNodeFilePath());
      resourceName = "libs/" + taskNodes.getNodeFilePath();
      programType = taskNodes.getProgramType();
      execClassName = taskNodes.getExecClassName();
    }
    this.pythonFileName = resourceName;

    if (nodeId.equals("101")) {
      // python节点 需要自己生成py脚本
      // 复制需要提交的py主文件
      String str = FileUtils.readResourceFile(Constants.ALGORITHM_PYTHON_TMPFILE);
      String info = algorithmParameters.getSettingParams();
      Map<String, Object> infoMap = JSONUtils.toObjectMap(info);
      String raw = MapUtils.getString(infoMap, "rawScript", "");
      str = str.replace("${rawScript}", raw);
      List list = Arrays.asList(str.split("\n"));
      try {
        File resFile = new File(taskExecutionContext.getExecutePath(), "tmp_python.py");
        org.apache.commons.io.FileUtils.writeLines(resFile, list);
      } catch (IOException e) {
        e.printStackTrace();
      }
      execClassName = "tmp_python.tmp_python";
    }

    if (programType != null && programType == 5) {
      // 复制libs目录下的依赖jar包
      FileUtils.copyResourceDirectory(taskExecutionContext.getExecutePath(), "libs", "jar");
    }

    // 生成json文件
    String jsonFilePath =
        InputJsonUtils.buildAlgoJsonFile(
            taskExecutionContext, logger, algorithmParameters, processService);
    // 设置文件名称参数
    String fileName = jsonFilePath.substring(jsonFilePath.lastIndexOf("/") + 1);
    if (StringUtils.isNotBlank(execClassName)) {
      fileName += " " + execClassName;
    }
    // 设置传入参数
    this.mainArgs = fileName;
  }

  private void downloadResource(String execLocalPath, String fullName) {
    try {

      IFileAdapter fileAdapter =
          FileAdapterUtils.getFileAdapterByProject(taskExecutionContext.getProjectId());
      fileAdapter.copyServerToLocal(
          fullName, execLocalPath + File.separator + fullName, false, true);
    } catch (Exception e) {
      logger.error(e.getMessage(), e);
      throw new RuntimeException(e.getMessage());
    }
  }

  @Override
  public void handle() throws Exception {
    try {
      //  construct process
      CommandExecuteResult commandExecuteResult = shellCommandExecutor.run(buildCommand());

      setExitStatusCode(commandExecuteResult.getExitStatusCode());
      setAppIds(commandExecuteResult.getAppIds());
      setProcessId(commandExecuteResult.getProcessId());
    } catch (Exception e) {
      logger.error("python task failure", e);
      setExitStatusCode(Constants.EXIT_CODE_FAILURE);
      throw e;
    }
  }

  @Override
  public void cancelApplication(boolean cancelApplication) throws Exception {
    // cancel process
    shellCommandExecutor.cancelApplication();
  }

  /**
   * build command
   *
   * @return raw python script
   * @throws Exception exception
   */
  private String buildCommand() throws Exception {

    List<String> parameterList = buildParameters();

    String command =
        ParameterUtils.convertParameterPlaceholders(
            String.join(" ", parameterList), taskExecutionContext.getDefinedParams());
    logger.info("task command: {}", command);

    return command;
  }

  /**
   * build parameters
   *
   * @return parameter list
   */
  private List<String> buildParameters() {

    List<String> result = new ArrayList<>();

    String pythonHome = ConfigUtils.getInstance().getString("python.home");
    if (StringUtils.isEmpty(pythonHome)) {
      pythonHome = PYTHON;
    }

    result.add(pythonHome);

    result.add(this.pythonFileName);

    // command args
    if (StringUtils.isNotEmpty(this.mainArgs)) {
      result.add(this.mainArgs);
    }
    return result;
  }

  @Override
  public AbstractParameters getParameters() {
    return algorithmParameters;
  }
}
